/*
 * Copyright (c) 2021 Futurewei Technologies, Inc.
 *
 * clang2mpl is licensed under Mulan PSL v2.
 * You can use this software according to the terms and conditions of the Mulan
 * PSL v2. You may obtain a copy of Mulan PSL v2 at:
 *
 *     http://license.coscl.org.cn/MulanPSL2
 *
 * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY
 * KIND, EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO
 * NON-INFRINGEMENT, MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. See the
 * Mulan PSL v2 for more details.
 */
// RUN: %clang2mpl --ascii --verify --simple-short-circuit %s -- -Wno-unused-value --target=aarch64-linux-elf
// RUN: cat %m | %FileCheck %s
void binop(int a, int b, unsigned c, unsigned d) {
  // CHECK: LOC 2 [[# @LINE + 2 ]]{{$}}
  // CHECK-NEXT: mul i32 (dread i32 %a, dread i32 %b)
  a * b;
  // CHECK: LOC 2 [[# @LINE + 2 ]]{{$}}
  // CHECK-NEXT: div i32 (dread i32 %a, dread i32 %b)
  a / b;
  // CHECK: LOC 2 [[# @LINE + 2 ]]{{$}}
  // CHECK-NEXT: rem i32 (dread i32 %a, dread i32 %b)
  a % b;
  // CHECK: LOC 2 [[# @LINE + 2 ]]{{$}}
  // CHECK-NEXT: add i32 (dread i32 %a, dread i32 %b)
  a + b;
  // CHECK: LOC 2 [[# @LINE + 2 ]]{{$}}
  // CHECK-NEXT: sub i32 (dread i32 %a, dread i32 %b)
  a - b;
  // CHECK: LOC 2 [[# @LINE + 2 ]]{{$}}
  // CHECK-NEXT: shl i32 (dread i32 %a, dread i32 %b)
  a << b;
  // CHECK: LOC 2 [[# @LINE + 2 ]]{{$}}
  // CHECK-NEXT: ashr i32 (dread i32 %a, dread i32 %b)
  a >> b;
  // CHECK: LOC 2 [[# @LINE + 2 ]]{{$}}
  // CHECK-NEXT: lshr u32 (dread u32 %c, dread u32 %d)
  c >> d;
  // DISABLED: LOC 2 [[# @LINE + 2 ]]{{$}}
  // DISABLED: cmp i32 (dread i32 %a, dread i32 %b)
  // a <=> b;
  // CHECK: LOC 2 [[# @LINE + 2 ]]{{$}}
  // CHECK-NEXT: lt i32 i32 (dread i32 %a, dread i32 %b)
  a < b;
  // CHECK: LOC 2 [[# @LINE + 2 ]]{{$}}
  // CHECK-NEXT: gt i32 i32 (dread i32 %a, dread i32 %b)
  a > b;
  // CHECK: LOC 2 [[# @LINE + 2 ]]{{$}}
  // CHECK-NEXT: le i32 i32 (dread i32 %a, dread i32 %b)
  a <= b;
  // CHECK: LOC 2 [[# @LINE + 2 ]]{{$}}
  // CHECK-NEXT: ge i32 i32 (dread i32 %a, dread i32 %b)
  a >= b;
  // CHECK: LOC 2 [[# @LINE + 2 ]]{{$}}
  // CHECK-NEXT: eq i32 i32 (dread i32 %a, dread i32 %b)
  a == b;
  // CHECK: LOC 2 [[# @LINE + 2 ]]{{$}}
  // CHECK-NEXT: ne i32 i32 (dread i32 %a, dread i32 %b)
  a != b;
  // CHECK: LOC 2 [[# @LINE + 2 ]]{{$}}
  // CHECK-NEXT: band i32 (dread i32 %a, dread i32 %b)
  a & b;
  // CHECK: LOC 2 [[# @LINE + 2 ]]{{$}}
  // CHECK-NEXT: bxor i32 (dread i32 %a, dread i32 %b)
  a ^ b;
  // CHECK: LOC 2 [[# @LINE + 2 ]]{{$}}
  // CHECK-NEXT: bior i32 (dread i32 %a, dread i32 %b)
  a | b;
  // CHECK: LOC 2 [[# @LINE + 4 ]]{{$}}
  // CHECK-NEXT: cand i32 (
  // CHECK-NEXT:    ne i32 i32 (dread i32 %a, constval i32 0),
  // CHECK-NEXT:    ne i32 i32 (dread i32 %b, constval i32 0))
  a && b;
  // CHECK: LOC 2 [[# @LINE + 4 ]]{{$}}
  // CHECK-NEXT: cior i32 (
  // CHECK-NEXT:    ne i32 i32 (dread i32 %a, constval i32 0),
  // CHECK-NEXT:    ne i32 i32 (dread i32 %b, constval i32 0))
  a || b;
}

void assign(int a) {
  int x;
  // CHECK: LOC 2 [[# @LINE + 4 ]]{{$}}
  // CHECK-NEXT: var %p <* i32>
  // CHECK: LOC 2 [[# @LINE + 2 ]]{{$}}
  // CHECK-NEXT: dassign %p 0 (addrof a64 %x)
  int *p = &x;
  // CHECK: LOC 2 [[# @LINE + 2 ]]{{$}}
  // CHECK-NEXT: dassign %x 0 (dread i32 %a)
  x = a;
  // CHECK: LOC 2 [[# @LINE + 2 ]]{{$}}
  // CHECK-NEXT: iassign <* i32> 0 (dread a64 %p, dread i32 %a)
  *p = a;
  // CHECK: LOC 2 [[# @LINE + 2 ]]{{$}}
  // CHECK-NEXT: dassign %x 0 (iread i32 <* i32> 0 (dread a64 %p))
  x = *p;
}

int pointers(int *a, int *b, int c, short *d, long long *e) {
  // CHECK: LOC 2 [[# @LINE + 4 ]]{{$}}
  // CHECK-NEXT: var %x i32
  // CHECK: LOC 2 [[# @LINE + 2 ]]{{$}}
  // CHECK-NEXT: dassign %x 0 (iread i32 <* i32> 0 (dread a64 %a))
  int x = *a;
  // CHECK: LOC 2 [[# @LINE + 4 ]]{{$}}
  // CHECK-NEXT: add i32 (
  // CHECK-NEXT: iread i32 <* i32> 0 (dread a64 %a),
  // CHECK-NEXT: iread i32 <* i32> 0 (dread a64 %b))
  *a + *b;
  // CHECK: LOC 2 [[# @LINE + 4 ]]{{$}}
  // CHECK-NEXT: iassign <* i32> 0 (
  // CHECK-NEXT: dread a64 %a,
  // CHECK-NEXT: iread i32 <* i32> 0 (dread a64 %b))
  *a = *b;
  // CHECK: LOC 2 [[# @LINE + 6 ]]{{$}}
  // CHECK-NEXT: add a64 (
  // CHECK-NEXT:    dread a64 %a,
  // CHECK-NEXT:    mul u64 (
  // CHECK-NEXT:      cvt a64 i32 (dread i32 %c),
  // CHECK-NEXT:      constval a64 4))
  a + c;
  // CHECK: LOC 2 [[# @LINE + 6 ]]{{$}}
  // CHECK-NEXT: add a64 (
  // CHECK-NEXT:    mul u64 (
  // CHECK-NEXT:      cvt a64 i32 (dread i32 %c),
  // CHECK-NEXT:      constval a64 2),
  // CHECK-NEXT:    dread a64 %d)
  c + d;
  // CHECK: LOC 2 [[# @LINE + 6 ]]{{$}}
  // CHECK-NEXT: sub a64 (
  // CHECK-NEXT:    dread a64 %e,
  // CHECK-NEXT:    mul u64 (
  // CHECK-NEXT:      cvt a64 i32 (dread i32 %c),
  // CHECK-NEXT:      constval a64 8))
  e - c;
  // CHECK: LOC 2 [[# @LINE + 4 ]]{{$}}
  // CHECK-NEXT: div i64 (
  // CHECK-NEXT:    sub i64 (dread a64 %a, dread a64 %b),
  // CHECK-NEXT:    constval i64 4)
  a - b;
  // CHECK: LOC 2 [[# @LINE + 2 ]]{{$}}
  // CHECK-NEXT: return (iread i32 <* i32> 0 (dread a64 %b))
  return *b;
}

void compoundAssign(int a, int b, unsigned c, unsigned d) {
  int x;
  // CHECK: LOC 2 [[# @LINE + 2 ]]{{$}}
  // CHECK-NEXT: dassign %a 0 (mul i32 (dread i32 %a, dread i32 %b))
  a *= b;
  // CHECK: LOC 2 [[# @LINE + 2 ]]{{$}}
  // CHECK-NEXT: dassign %a 0 (div i32 (dread i32 %a, dread i32 %b))
  a /= b;
  // CHECK: LOC 2 [[# @LINE + 2 ]]{{$}}
  // CHECK-NEXT: dassign %a 0 (rem i32 (dread i32 %a, dread i32 %b))
  a %= b;
  // CHECK: LOC 2 [[# @LINE + 2 ]]{{$}}
  // CHECK-NEXT: dassign %a 0 (add i32 (dread i32 %a, dread i32 %b))
  a += b;
  // CHECK: LOC 2 [[# @LINE + 2 ]]{{$}}
  // CHECK-NEXT: dassign %a 0 (sub i32 (dread i32 %a, dread i32 %b))
  a -= b;
  // CHECK: LOC 2 [[# @LINE + 2 ]]{{$}}
  // CHECK-NEXT: dassign %a 0 (shl i32 (dread i32 %a, dread i32 %b))
  a <<= b;
  // CHECK: LOC 2 [[# @LINE + 2 ]]{{$}}
  // CHECK-NEXT: dassign %a 0 (ashr i32 (dread i32 %a, dread i32 %b))
  a >>= b;
  // CHECK: LOC 2 [[# @LINE + 2 ]]{{$}}
  // CHECK-NEXT: dassign %c 0 (lshr u32 (dread u32 %c, dread u32 %d))
  c >>= d;
  // CHECK: LOC 2 [[# @LINE + 2 ]]{{$}}
  // CHECK-NEXT: dassign %a 0 (band i32 (dread i32 %a, dread i32 %b))
  a &= b;
  // CHECK: LOC 2 [[# @LINE + 2 ]]{{$}}
  // CHECK-NEXT: dassign %a 0 (bxor i32 (dread i32 %a, dread i32 %b))
  a ^= b;
  // CHECK: LOC 2 [[# @LINE + 2 ]]{{$}}
  // CHECK-NEXT: dassign %a 0 (bior i32 (dread i32 %a, dread i32 %b))
  a |= b;
  // CHECK: LOC 2 [[# @LINE + 3 ]]{{$}}
  // CHECK-NEXT: dassign %a 0 (add i32 (dread i32 %a, dread i32 %b))
  // CHECK-NEXT: dassign %x 0 (dread i32 %a)
  x = a += b;
}


int g1, g2;
int gArr1[4], gArr2[8];
struct A {
  int x;
  int y;
} gA1, gA2;
int foo();

void assignAssign() {
  // CHECK: LOC 2 [[# @LINE + 3 ]]{{$}}
  // CHECK-NEXT: dassign $g2 0 (constval i32 4)
  // CHECK-NEXT: dassign $g1 0 (dread i32 $g2)
  g1 = g2 = 4;
  // CHECK: LOC 2 [[# @LINE + 7 ]]{{$}}
  // CHECK-NEXT: iassign <* i32> 0 (
  // CHECK-NEXT:   array 0 a64 <* <[8] i32>> (addrof a64 $gArr2, constval i32 3),
  // CHECK-NEXT:   dread i32 $g1)
  // CHECK-NEXT: iassign <* i32> 0 (
  // CHECK-NEXT:   array 0 a64 <* <[4] i32>> (addrof a64 $gArr1, constval i32 2),
  // CHECK-NEXT:   iread i32 <* i32> 0 (array 0 a64 <* <[8] i32>> (addrof a64 $gArr2, constval i32 3)))
  gArr1[2] = gArr2[3] = g1;
  // CHECK: LOC 2 [[# @LINE + 3 ]]{{$}}
  // CHECK-NEXT: dassign $gA2 1 (dread i32 $g2)
  // CHECK-NEXT: dassign $gA1 2 (dread i32 $gA2 1)
  gA1.y = gA2.x = g2;
  // CHECK: LOC 2 [[# @LINE + 6 ]]{{$}}
  // CHECK-NEXT: callassigned &foo () { dassign %_result[[#RESNUM:]] 0 }
  // CHECK-NEXT: dassign $g2 0 (dread i32 %_result[[#RESNUM]])
  // CHECK-NEXT: iassign <* i32> 0 (
  // CHECK-NEXT:   array 0 a64 <* <[4] i32>> (addrof a64 $gArr1, constval i32 0),
  // CHECK-NEXT:   dread i32 $g2)
  gArr1[0] = g2 = foo();
}
